import re
from django.core.cache import cache

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler

from user import models
from django.conf import settings


class LoginUserModelSerializer(serializers.ModelSerializer):
    username = serializers.CharField()

    class Meta:
        model = models.User
        fields = ['id', 'username', 'password']
        extra_kwargs = {
            'id': {'read_only': True},
            'password': {'write_only': True}
        }

    def validate(self, attrs):
        """
        全局钩子函数
        """
        user = self.__get_user(attrs)
        token = self.__get_token(user)
        self.context['user'] = user
        self.context['token'] = token

        return attrs

    def __get_user(self, attrs):
        """
        获取用户
        """
        username = attrs.get('username')
        password = attrs.get('password')
        if re.match(r'1[3-9][0-9]{9}', username):
            user = models.User.objects.filter(telephone=username).first()
        elif re.match(r"^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$", username):
            user = models.User.objects.filter(email=username).first()
        else:
            user = models.User.objects.filter(username=username).first()

        if user:
            if user.check_password(password):
                return user
            else:
                raise ValidationError('密码错误')
        else:
            raise ValidationError(f'{username}不存在')

    def __get_token(self, user):
        """
        签发token
        """
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token


class CodeUserModelSerializer(serializers.ModelSerializer):

    code = serializers.CharField(required=True, write_only=True)
    telephone = serializers.CharField(max_length=11, min_length=11)

    class Meta:
        model = models.User
        fields = ['telephone', 'code']

    def validate(self, attrs):
        """
        全局钩子
        """
        user = self.__get_user(attrs)  # 获取用户
        token = self.__get_token(user)  # 获取token

        self.context['token'] = token
        self.context['user'] = user

        return attrs

    def __get_user(self, attrs):
        """
        获取用户
        """
        telephone = attrs.get('telephone')
        code = attrs.get('code')
        # 取出code
        cache_code = cache.get(settings.CODE_CACHE_KEY % {'telephone': telephone})
        if code == cache_code:  # 验证码校验通过，进行用户检测

            if re.match(r'1[3-9][0-9]{9}', telephone):
                try:
                    user = models.User.objects.get(telephone=telephone)
                    cache.set(settings.CODE_CACHE_KEY % {'telephone': telephone}, '')
                    return user
                except:
                    raise ValidationError('手机号不存在，请先注册')
            else:
                raise ValidationError('手机号不合法')

        raise ValidationError('验证码不正确')

    def __get_token(self, user):
        """
        签发token
        """
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token


class RegisterUserModelSerializer(serializers.ModelSerializer):

    code = serializers.CharField(max_length=4, min_length=4, write_only=True)

    class Meta:
        model = models.User
        fields = ['telephone', 'code', 'password', 'username']
        extra_kwargs = {
            'password': {'write_only': True, 'max_length': 18, 'min_length': 8},
            'username': {'read_only': True}
        }

    def validate_password(self, data):
        """
        校验密码是否合法
        1.密码必须由字母、数字、特殊符号组成，区分大小写
        2.特殊符号包含（. _ ~ ! @ # $ / ^ & *）
        3.密码长度为8-18位
        :param data: 校验通过的密码
        """
        if re.match(r'^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[._~!@#$^&*/])[A-Za-z0-9._~!@#$^&*/]{8,18}$', data):
            return data

        raise ValidationError('密码必须由字母(区分大小写)、数字、特殊符号(. _ ~ ! @ # $ / ^ & *)，长度必须再8~18之间')

    def validate(self, attrs):
        """
        全局钩子, 用于校验code和手机号，并设置用户名为手机号
        """
        telephone = attrs.get('telephone')
        code = attrs.get('code')
        cache_code = cache.get(settings.CODE_CACHE_KEY % {'telephone': telephone})
        if re.match(r"^1[3-9][0-9]{9}$", telephone):
            # 手机号校验通过
            if code == cache_code:
                # 验证码校验通过
                attrs.pop('code')
                attrs['username'] = telephone
                return attrs
            else:
                raise ValidationError('验证码错误')
        else:
            raise ValidationError('手机号不合法')

    def create(self, validated_data):
        """
        重写create方法, 保存用户
        """
        user = models.User.objects.create_user(**validated_data)
        return user
